{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: torch==2.2 in /opt/conda/lib/python3.10/site-packages (2.2.0)\n",
      "Requirement already satisfied: filelock in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (3.13.1)\n",
      "Requirement already satisfied: typing-extensions>=4.8.0 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (4.9.0)\n",
      "Requirement already satisfied: sympy in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (1.12)\n",
      "Requirement already satisfied: networkx in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2.8.5)\n",
      "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (3.1.2)\n",
      "Requirement already satisfied: fsspec in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2023.12.2)\n",
      "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (8.9.2.26)\n",
      "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.3.1)\n",
      "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (11.0.2.54)\n",
      "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (10.3.2.106)\n",
      "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (11.4.5.107)\n",
      "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.0.106)\n",
      "Requirement already satisfied: nvidia-nccl-cu12==2.19.3 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2.19.3)\n",
      "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: triton==2.2.0 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2.2.0)\n",
      "Requirement already satisfied: nvidia-nvjitlink-cu12 in /opt/conda/lib/python3.10/site-packages (from nvidia-cusolver-cu12==11.4.5.107->torch==2.2) (12.3.101)\n",
      "Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->torch==2.2) (2.1.3)\n",
      "Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.10/site-packages (from sympy->torch==2.2) (1.3.0)\n",
      "Requirement already satisfied: torchvision==0.17.0 in /opt/conda/lib/python3.10/site-packages (0.17.0)\n",
      "Requirement already satisfied: numpy in /opt/conda/lib/python3.10/site-packages (from torchvision==0.17.0) (1.26.2)\n",
      "Requirement already satisfied: requests in /opt/conda/lib/python3.10/site-packages (from torchvision==0.17.0) (2.28.1)\n",
      "Requirement already satisfied: torch==2.2.0 in /opt/conda/lib/python3.10/site-packages (from torchvision==0.17.0) (2.2.0)\n",
      "Requirement already satisfied: pillow!=8.3.*,>=5.3.0 in /opt/conda/lib/python3.10/site-packages (from torchvision==0.17.0) (9.3.0)\n",
      "Requirement already satisfied: filelock in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (3.13.1)\n",
      "Requirement already satisfied: typing-extensions>=4.8.0 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (4.9.0)\n",
      "Requirement already satisfied: sympy in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (1.12)\n",
      "Requirement already satisfied: networkx in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (2.8.5)\n",
      "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (3.1.2)\n",
      "Requirement already satisfied: fsspec in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (2023.12.2)\n",
      "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (8.9.2.26)\n",
      "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (12.1.3.1)\n",
      "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (11.0.2.54)\n",
      "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (10.3.2.106)\n",
      "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (11.4.5.107)\n",
      "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (12.1.0.106)\n",
      "Requirement already satisfied: nvidia-nccl-cu12==2.19.3 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (2.19.3)\n",
      "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (12.1.105)\n",
      "Requirement already satisfied: triton==2.2.0 in /opt/conda/lib/python3.10/site-packages (from torch==2.2.0->torchvision==0.17.0) (2.2.0)\n",
      "Requirement already satisfied: nvidia-nvjitlink-cu12 in /opt/conda/lib/python3.10/site-packages (from nvidia-cusolver-cu12==11.4.5.107->torch==2.2.0->torchvision==0.17.0) (12.3.101)\n",
      "Requirement already satisfied: charset-normalizer<3,>=2 in /opt/conda/lib/python3.10/site-packages (from requests->torchvision==0.17.0) (2.1.0)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.10/site-packages (from requests->torchvision==0.17.0) (3.3)\n",
      "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /opt/conda/lib/python3.10/site-packages (from requests->torchvision==0.17.0) (1.26.10)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.10/site-packages (from requests->torchvision==0.17.0) (2022.6.15)\n",
      "Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->torch==2.2.0->torchvision==0.17.0) (2.1.3)\n",
      "Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.10/site-packages (from sympy->torch==2.2.0->torchvision==0.17.0) (1.3.0)\n",
      "Requirement already satisfied: Pillow==9.3.0 in /opt/conda/lib/python3.10/site-packages (9.3.0)\n"
     ]
    }
   ],
   "source": [
    "!pip install torch==2.2\n",
    "!pip install torchvision==0.17.0\n",
    "!pip install Pillow==9.3.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
      "  _torch_pytree._register_pytree_node(\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision import datasets, transforms\n",
    "\n",
    "import numpy as np\n",
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ConvNet(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(ConvNet, self).__init__()\n",
    "        self.cn1 = nn.Conv2d(1, 16, 3, 1)\n",
    "        self.cn2 = nn.Conv2d(16, 32, 3, 1)\n",
    "        self.dp1 = nn.Dropout2d(0.10)\n",
    "        self.dp2 = nn.Dropout2d(0.25)\n",
    "        self.fc1 = nn.Linear(4608, 64) # 4608 is basically 12 X 12 X 32\n",
    "        self.fc2 = nn.Linear(64, 10)\n",
    " \n",
    "    def forward(self, x):\n",
    "        x = self.cn1(x)\n",
    "        x = F.relu(x)\n",
    "        x = self.cn2(x)\n",
    "        x = F.relu(x)\n",
    "        x = F.max_pool2d(x, 2)\n",
    "        x = self.dp1(x)\n",
    "        x = torch.flatten(x, 1)\n",
    "        x = self.fc1(x)\n",
    "        x = F.relu(x)\n",
    "        x = self.dp2(x)\n",
    "        x = self.fc2(x)\n",
    "        op = F.log_softmax(x, dim=1)\n",
    "        return op\n",
    "    \n",
    "model = ConvNet()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "PATH_TO_MODEL = \"./convnet.pth\"\n",
    "model.load_state_dict(torch.load(PATH_TO_MODEL, map_location=\"cpu\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.eval()\n",
    "for p in model.parameters():\n",
    "    p.requires_grad_(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:1347: UserWarning: dropout2d: Received a 2-D input to dropout2d, which is deprecated and will result in an error in a future release. To retain the behavior and silence this warning, please use dropout instead. Note that dropout2d exists to provide channel-wise dropout on inputs with 2 spatial dimensions, a channel dimension, and an optional batch dimension (i.e. 3D or 4D inputs).\n",
      "  warnings.warn(warn_msg)\n"
     ]
    }
   ],
   "source": [
    "demo_input = torch.ones(1, 1, 28, 28)\n",
    "traced_model = torch.jit.trace(model, demo_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "graph(%self.1 : __torch__.ConvNet,\n",
       "      %x.1 : Float(1, 1, 28, 28, strides=[784, 784, 28, 1], requires_grad=0, device=cpu)):\n",
       "  %fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_2.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n",
       "  %dp2 : __torch__.torch.nn.modules.dropout.___torch_mangle_1.Dropout2d = prim::GetAttr[name=\"dp2\"](%self.1)\n",
       "  %fc1 : __torch__.torch.nn.modules.linear.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n",
       "  %dp1 : __torch__.torch.nn.modules.dropout.Dropout2d = prim::GetAttr[name=\"dp1\"](%self.1)\n",
       "  %cn2 : __torch__.torch.nn.modules.conv.___torch_mangle_0.Conv2d = prim::GetAttr[name=\"cn2\"](%self.1)\n",
       "  %cn1 : __torch__.torch.nn.modules.conv.Conv2d = prim::GetAttr[name=\"cn1\"](%self.1)\n",
       "  %123 : Tensor = prim::CallMethod[name=\"forward\"](%cn1, %x.1)\n",
       "  %input.3 : Float(1, 16, 26, 26, strides=[10816, 676, 26, 1], requires_grad=0, device=cpu) = aten::relu(%123) # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:1473:0\n",
       "  %124 : Tensor = prim::CallMethod[name=\"forward\"](%cn2, %input.3)\n",
       "  %input.7 : Float(1, 32, 24, 24, strides=[18432, 576, 24, 1], requires_grad=0, device=cpu) = aten::relu(%124) # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:1473:0\n",
       "  %70 : int = prim::Constant[value=2]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %71 : int = prim::Constant[value=2]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %72 : int[] = prim::ListConstruct(%70, %71)\n",
       "  %73 : int[] = prim::ListConstruct()\n",
       "  %74 : int = prim::Constant[value=0]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %75 : int = prim::Constant[value=0]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %76 : int[] = prim::ListConstruct(%74, %75)\n",
       "  %77 : int = prim::Constant[value=1]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %78 : int = prim::Constant[value=1]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %79 : int[] = prim::ListConstruct(%77, %78)\n",
       "  %80 : bool = prim::Constant[value=0]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %input.9 : Float(1, 32, 12, 12, strides=[4608, 144, 12, 1], requires_grad=0, device=cpu) = aten::max_pool2d(%input.7, %72, %73, %76, %79, %80) # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:796:0\n",
       "  %125 : Tensor = prim::CallMethod[name=\"forward\"](%dp1, %input.9)\n",
       "  %85 : int = prim::Constant[value=1]() # /var/tmp/ipykernel_2981674/2721400238.py:18:0\n",
       "  %86 : int = prim::Constant[value=-1]() # /var/tmp/ipykernel_2981674/2721400238.py:18:0\n",
       "  %input.11 : Float(1, 4608, strides=[4608, 1], requires_grad=0, device=cpu) = aten::flatten(%125, %85, %86) # /var/tmp/ipykernel_2981674/2721400238.py:18:0\n",
       "  %126 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %input.11)\n",
       "  %input.15 : Float(1, 64, strides=[64, 1], requires_grad=0, device=cpu) = aten::relu(%126) # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:1473:0\n",
       "  %127 : Tensor = prim::CallMethod[name=\"forward\"](%dp2, %input.15)\n",
       "  %128 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %127)\n",
       "  %94 : int = prim::Constant[value=1]() # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:1947:0\n",
       "  %95 : NoneType = prim::Constant()\n",
       "  %96 : Float(1, 10, strides=[10, 1], requires_grad=0, device=cpu) = aten::log_softmax(%128, %94, %95) # /opt/conda/lib/python3.10/site-packages/torch/nn/functional.py:1947:0\n",
       "  return (%96)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "traced_model.graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "def forward(self,\n",
      "    x: Tensor) -> Tensor:\n",
      "  fc2 = self.fc2\n",
      "  dp2 = self.dp2\n",
      "  fc1 = self.fc1\n",
      "  dp1 = self.dp1\n",
      "  cn2 = self.cn2\n",
      "  cn1 = self.cn1\n",
      "  input = torch.relu((cn1).forward(x, ))\n",
      "  input0 = torch.relu((cn2).forward(input, ))\n",
      "  input1 = torch.max_pool2d(input0, [2, 2], annotate(List[int], []), [0, 0], [1, 1])\n",
      "  input2 = torch.flatten((dp1).forward(input1, ), 1)\n",
      "  input3 = torch.relu((fc1).forward(input2, ))\n",
      "  _0 = (fc2).forward((dp2).forward(input3, ), )\n",
      "  return torch.log_softmax(_0, 1)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(traced_model.code)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.jit.save(traced_model, 'traced_convnet.pt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "loaded_traced_model = torch.jit.load('traced_convnet.pt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "image = Image.open(\"./digit_image.jpg\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def image_to_tensor(image):\n",
    "    gray_image = transforms.functional.to_grayscale(image)\n",
    "    resized_image = transforms.functional.resize(gray_image, (28, 28))\n",
    "    input_image_tensor = transforms.functional.to_tensor(resized_image)\n",
    "    input_image_tensor_norm = transforms.functional.normalize(input_image_tensor, (0.1302,), (0.3069,))\n",
    "    return input_image_tensor_norm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_tensor = image_to_tensor(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-1.0458e+01, -1.3929e+01, -2.5734e-03, -8.8133e+00, -1.0267e+01,\n",
       "         -1.5833e+01, -1.2593e+01, -1.3940e+01, -6.0533e+00, -1.2960e+01]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "loaded_traced_model(input_tensor.unsqueeze(0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-1.0458e+01, -1.3929e+01, -2.5734e-03, -8.8133e+00, -1.0267e+01,\n",
       "         -1.5833e+01, -1.2593e+01, -1.3940e+01, -6.0533e+00, -1.2960e+01]])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model(input_tensor.unsqueeze(0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.mobile_optimizer import optimize_for_mobile"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimized_traced_model = optimize_for_mobile(traced_model)\n",
    "optimized_traced_model._save_for_lite_interpreter(\"./optimized_for_mobile_traced_model.pt\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.jit.mobile import (\n",
    "    _backport_for_mobile,\n",
    "    _get_model_bytecode_version,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "model version 8\n",
      "new model version 5\n"
     ]
    }
   ],
   "source": [
    "MODEL_INPUT_FILE = \"optimized_for_mobile_traced_model.pt\"\n",
    "MODEL_OUTPUT_FILE = \"optimized_for_mobile_traced_model_v5.ptl\"\n",
    "\n",
    "print(\"model version\", _get_model_bytecode_version(f_input=MODEL_INPUT_FILE))\n",
    "\n",
    "_backport_for_mobile(f_input=MODEL_INPUT_FILE, f_output=MODEL_OUTPUT_FILE, to_version=5)\n",
    "\n",
    "print(\"new model version\", _get_model_bytecode_version(MODEL_OUTPUT_FILE))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (Local)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
